package com.un.ebs.production.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.un.ebs.core.exception.BusinessEntityNotFoundException;
import com.un.ebs.core.exception.BusinessException;
import com.un.ebs.core.util.SecurityUtils;
import com.un.ebs.core.util.StringUtils;
import com.un.ebs.inventory.domain.InventoryNoteItemExt;
import com.un.ebs.inventory.domain.Warehouse;
import com.un.ebs.inventory.enums.InventoryTypeEnum;
import com.un.ebs.inventory.service.InventoryNoteService;
import com.un.ebs.inventory.service.WarehouseService;
import com.un.ebs.product.enums.ProductPurchaseTypeEnum;
import com.un.ebs.production.domain.*;
import com.un.ebs.production.mapper.*;
import com.un.ebs.production.service.ProductionSeqService;
import com.un.ebs.production.service.ProductionWorkOrderService;
import com.un.ebs.purchase.domain.PurchaseOrder;
import com.un.ebs.purchase.domain.PurchaseOrderItemExt;
import com.un.ebs.purchase.dto.PurchaseOrderVo;
import com.un.ebs.purchase.service.PurchaseOrderService;
import com.un.ebs.sale.enums.SaleOrderTypeEnum;
import com.un.ebs.sys.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (TbProductionWorkOrder)表服务实现类
 *
 * @author liubo
 * @since 2020-07-07 13:22:25
 */
@Service("tbProductionWorkOrderService")
public class ProductionWorkOrderServiceImpl extends ServiceImpl<ProductionWorkOrderMapper, ProductionWorkOrder> implements ProductionWorkOrderService {

    @Resource
    private ProductionWorkOrderExtMapper productionWorkOrderExtMapper;

    @Resource
    private ProductionOrderItemMapper productionOrderItemMapper;

    @Resource
    private ProductionOrderMapper productionOrderMapper;

    @Resource
    private ProductionOrderViewMapper productionOrderViewMapper;

    @Resource
    private ProductionOrderItemBomExtMapper productionOrderItemBomExtMapper;

    @Resource
    private ProductionSeqService productionSeqService;

    @Resource
    private PurchaseOrderService purchaseOrderService;

    @Resource
    private InventoryNoteService inventoryNoteService;

    @Resource
    private WarehouseService warehouseService;

    @Resource
    private UserService userService;

    @Override
    public void start(String id) {
        ProductionWorkOrder order = baseMapper.selectById(id);
        if (order == null) {
            throw new BusinessEntityNotFoundException(id, "生产订单");
        }
        order.setRealStartTime(new Date());
        if (StringUtils.isNullOrEmpty(order.getWorkPersonnelId())) {
            order.setWorkPersonnelId(SecurityUtils.getUserContent().getPersonnelId());
            order.setWorkPersonnelName(SecurityUtils.getUserContent().getPersonnelName());
        }
        order.updateById();
    }

    @Override
    public void complete(String id) {
        ProductionWorkOrder order = baseMapper.selectById(id);
        if (order == null) {
            throw new BusinessEntityNotFoundException(id, "生产订单");
        }
        complete(order);
    }

    @Override
    public void completeFix() {
        LambdaQueryWrapper<ProductionOrderView> lq = Wrappers.lambdaQuery();
        lq.eq(ProductionOrderView::getProductionCompleteStatus, true);
        lq.eq(ProductionOrderView::getPlanWorkStatus, true);
        lq.eq(ProductionOrderView::getIsDelete, false);
        lq.eq(ProductionOrderView::getSubmitStatus, true);
        lq.orderByAsc(ProductionOrderView::getCreateTime);
        List<ProductionOrderView> list = productionOrderViewMapper.selectList(lq);

        for (ProductionOrderView production : list) {
            List<ProductionWorkOrderExt> workOrders = query(production.getId()).stream().filter(s -> s.getSeqItemPurchaseType().equals(ProductPurchaseTypeEnum.Purchase)).collect(Collectors.toList());
            for (ProductionWorkOrderExt order : workOrders) {
                PurchaseOrderItemExt purchaseOrderItem = purchaseOrderService.queryItemByProductionItem(order.getProductionOrderItemId());
                if (purchaseOrderItem == null) {
                    System.out.println(String.format("补录采购订单，生产订单为：%s", production.getOrderId()));
                    System.out.println();
                    purchaseOrderService.addByService(production.getOrderId(), production.getId(), order.getProductionSeqItemId());
                    purchaseOrderItem = purchaseOrderService.queryItemByProductionItem(order.getProductionOrderItemId());
                }
                PurchaseOrderVo purchaseOrder = purchaseOrderService.queryViewById(purchaseOrderItem.getOrderId());
                if (!purchaseOrder.getSubmitStatus()) {
                    throw new BusinessException("委外采购订单未提交。");
                }
                if (purchaseOrder.getItems().size() > 1) {
                    throw new BusinessException("委外采购订单条目数量大于1。");
                }
                if (!purchaseOrder.getIsServices()) {
                    throw new BusinessException("委外采购订单类型不正确。");
                }
                PurchaseOrder obj = purchaseOrderService.queryById(purchaseOrder.getId());
                if (!obj.getDeliverStatus()) {
                    obj.setDeliverAt(SecurityUtils.getUserContent().getId());
                    obj.setDeliverAtName(SecurityUtils.getUserContent().getPersonnelName());
                    obj.setDeliverTime(new Date());
                    obj.setDeliverStatus(true);
                    obj.updateById();
                }

                if (inventoryNoteService.queryItemExtByRelationNoteId(purchaseOrder.getId()).size() <= 0) {
                    Optional<Warehouse> warehouse = warehouseService.queryMatierial().stream().filter(s -> s.getIsReceive()).findFirst();
                    if (!warehouse.isPresent()) {
                        throw new BusinessException("缺少一个有效的物料仓库，且该物料仓库应为收货仓库。");
                    }

                    System.out.println("修复：");
                    System.out.println(String.format("采购订单：%s ,  生产订单：%s , 物料ID：%s", purchaseOrder.getId(), production.getOrderId(), purchaseOrderItem.getSkuId()));
                    inventoryNoteService.addServicePurchaseInAndOut(purchaseOrder.getId(), production.getOrderId(), warehouse.get().getId(), purchaseOrderItem.getSkuId(), purchaseOrderItem.getQty());
                    System.out.println();
                }
            }

        }
    }

    public void complete(ProductionWorkOrder order) {

        //检查前面的工序是否完工
        frontHasCompleteStatus(order);

        if (order.getCompletedStatus()) {
            throw new BusinessException("不能重复完工确认。");
        }

        ProductionOrderView productionOrderView = productionOrderViewMapper.selectById(order.getProductionOrderItemId());
        if (productionOrderView == null) {
            throw new BusinessEntityNotFoundException(order.getProductionOrderItemId(), "生产订单没有找到。");
        }

        if (productionOrderView.getPurchaseType() == ProductPurchaseTypeEnum.Produce && productionOrderView.getSaleOrderType() == SaleOrderTypeEnum.SALE) {
            //检查物料是否领用
            LambdaQueryWrapper<ProductionOrderItemBomExt> lq = Wrappers.lambdaQuery();
            lq.eq(ProductionOrderItemBomExt::getId, order.getProductionOrderItemId());
            double diff = productionOrderItemBomExtMapper.selectList(lq).stream().mapToDouble(s -> s.getRequirementQty() - s.getProductionUserQty()).sum();
            if (diff > 0) {
                Optional<ProductionOrderItemBomExt> orderItem = productionOrderItemBomExtMapper.selectList(lq).stream().filter(s -> s.getRequirementQty() - s.getProductionUserQty() > 0).findFirst();
                if (orderItem.isPresent()) {
                    throw new BusinessException(String.format("生产订单中 【%s : %s】 还没有领完料。", orderItem.get().getSkuCode(), orderItem.get().getNameAs()));
                } else {
                    throw new BusinessException("生产订单还没有领完料。");
                }
            }
        }

        ProductionOrderItem productionOrderItem = productionOrderItemMapper.selectById(order.getProductionOrderItemId());
        if (productionOrderItem == null) {
            throw new BusinessException("工单所关联的生产订单条目无效。");
        }

        // 检查委外工序是否有采购订单
        checkSeqSku(order, productionOrderView);

        if (order.getRealStartTime() == null) {
            order.setRealStartTime(new Date());
        }
        if (StringUtils.isNullOrEmpty(order.getWorkPersonnelId())) {
            order.setWorkPersonnelId(SecurityUtils.getUserContent().getPersonnelId());
            order.setWorkPersonnelName(SecurityUtils.getUserContent().getPersonnelName());
        }
        order.setRealEndTime(new Date());

        order.setCompletedPersonnelId(SecurityUtils.getUserContent().getPersonnelId());
        order.setCompletedPersonnelName(SecurityUtils.getUserContent().getPersonnelName());
        order.setCompletedStatus(true);
        order.updateById();
    }

    /**
     * 检查委外工序是否执行了采购、入库、领料
     *
     * @param order
     * @param productionOrderView
     */
    private void checkSeqSku(ProductionWorkOrder order, ProductionOrderView productionOrderView) {
        ProductionSeqItemView seqItemView = productionSeqService.queryItemById(order.getProductionSeqItemId());
        if (seqItemView == null) {
            throw new BusinessEntityNotFoundException(order.getProductionSeqItemId(), "工序");
        }
        if (seqItemView.getPurchaseType() != ProductPurchaseTypeEnum.Purchase) {
            return;
        }
        if (StringUtils.isNullOrEmpty(seqItemView.getRelationSkuId())) {
            throw new BusinessException("委外工序没有指定关联物料编码。");
        }

        // 查询采购情况
        List<PurchaseOrderItemExt> purchaseItems = purchaseOrderService.queryItemByProductionOrder(productionOrderView.getOrderId())
                .stream().filter(s -> seqItemView.getRelationSkuId().equals(s.getSkuId())).collect(Collectors.toList());

        if (StringUtils.isNullOrEmpty(purchaseItems)) {
            throw new BusinessException(String.format("委外工序【%s：%s】所关联的物料【%s：%s】没有生成采购订单。", seqItemView.getCode(), seqItemView.getName(), seqItemView.getSkuCode(), seqItemView.getSkuName()));
        }

        // 查询收货情况
        Double reqQty = order.getPlanWorkQty() * seqItemView.getRelationUserQty();
        Double purchaseQty = purchaseItems.stream().mapToDouble(s -> s.getQty()).sum();
        if (reqQty.intValue() != purchaseQty.intValue()) {
            throw new BusinessException(String.format("委外工序【%s：%s】所关联的物料【%s：%s】对应的采购数量不正确，BOM需求量%s：采购订单【%s】数量：%s。",
                    seqItemView.getCode(), seqItemView.getName(), seqItemView.getSkuCode(), seqItemView.getSkuName(), reqQty, purchaseItems.get(0).getCode(), purchaseQty));
        }
        Double deliverQty = purchaseItems.stream().mapToDouble(s -> s.getDeliverQty()).sum();
        if (purchaseQty.intValue() != deliverQty.intValue()) {
            throw new BusinessException(String.format("委外工序【%s：%s】所关联的物料【%s：%s】的实际采购接收数量【%s】与计划采购数量【%s】不符。",
                    seqItemView.getCode(), seqItemView.getName(), seqItemView.getSkuCode(), seqItemView.getSkuName(), deliverQty.intValue(), purchaseQty.intValue()));
        }

        // 查询领料情况
        List<InventoryTypeEnum> types = new ArrayList<>();
        types.add(InventoryTypeEnum.PRODUCTION_BOM_USER);
        types.add(InventoryTypeEnum.REPAIR_USER);
        double outQty = inventoryNoteService.queryItemExtByRelationNoteId(productionOrderView.getOrderId()).stream().filter(s -> types.contains(s.getNoteType()) && s.getSubmitStatus() && s.getAuditStatus() && s.getSkuId().equals(seqItemView.getRelationSkuId())).mapToDouble(s -> s.getQty()).sum();
        if (deliverQty > outQty) {
            throw new BusinessException(String.format("委外工序【%s：%s】所关联的物料【%s：%s】未出库，收货数量%s与领料数量%s不一致。", seqItemView.getCode(), seqItemView.getName(), seqItemView.getSkuCode(), seqItemView.getSkuName(), deliverQty, outQty));
        }
    }

    @Transactional
    @Override
    public void completeOrder(String productionOrderId) {
        ProductionOrder order = productionOrderMapper.selectById(productionOrderId);
        if (order == null) {
            throw new BusinessEntityNotFoundException(productionOrderId, "生产订单");
        }
        if (order.getDeliverStatus()) {
            throw new BusinessException("生产订单已经交付，不能重复执行。");
        }

        LambdaQueryWrapper<ProductionOrderItem> lq = Wrappers.lambdaQuery();
        lq.eq(ProductionOrderItem::getOrderId, productionOrderId);
        List<ProductionOrderItem> list = productionOrderItemMapper.selectList(lq);
        for (ProductionOrderItem item : list) {
            completeOrderItem(item.getId());
        }

        // 生产订单交付
        order.setDeliverStatus(true);
        order.setDeliverAt(SecurityUtils.getUserContent().getId());
        order.setDeliverAtName(SecurityUtils.getUserContent().getPersonnelName());
        order.setDeliverTime(new Date());
        order.updateById();
    }

    private void frontHasCompleteStatus(ProductionWorkOrder order) {
        ProductionWorkOrderExt orderExt = queryViewById(order.getId());
        if (orderExt == null) {
            throw new BusinessException("数据异常，工单视图中没有找到相应的工单。");
        }

        if (!userService.hasRole(orderExt.getRoleId())) {
            throw new BusinessException(String.format("你不是【%s】成员，不能执行此操作。", orderExt.getRoleName()));
        }

        List<ProductionWorkOrderExt> list = baseMapper.selectByItemId(order.getProductionOrderItemId())
                .stream()
                .filter(s -> Integer.valueOf(s.getProductionSeqItemCode()) < Integer.valueOf(orderExt.getProductionSeqItemCode()))
                .filter(s -> s.getCompletedStatus() == false)
                .sorted(Comparator.comparing(t -> t.getProductionSeqItemCode())).collect(Collectors.toList());

        Collections.reverse(list);
        Optional<ProductionWorkOrderExt> obj = list.stream().findFirst();
        if (obj.isPresent()) {
            throw new BusinessException(String.format("之前的工序【%s】还未完工确认，必须按顺序逐个确认。", obj.get().getProductionSeqItemCode()));
        }
    }


    @Transactional
    @Override
    public void completeOrderItem(String productionOrderItemId) {
        LambdaQueryWrapper<ProductionWorkOrder> lq = Wrappers.lambdaQuery();
        lq.eq(ProductionWorkOrder::getProductionOrderItemId, productionOrderItemId);
        List<ProductionWorkOrder> list = baseMapper.selectList(lq);
        if (StringUtils.isNullOrEmpty(list)) {
            throw new BusinessException("产品没有工序集。");
        }
        for (ProductionWorkOrder order : list) {
            complete(order);
        }
    }

    @Override
    public IPage<ProductionWorkOrderExt> query(String searchText, String productionOrderItemId, int pageSize,
                                               int pageIndex) {
        Page<ProductionWorkOrderExt> page = new Page(pageIndex, pageSize);
        LambdaQueryWrapper<ProductionWorkOrderExt> lq = Wrappers.lambdaQuery();
        if (!StringUtils.isNullOrEmpty(productionOrderItemId)) {
            lq.eq(ProductionWorkOrderExt::getProductionOrderItemId, productionOrderItemId);
            if (!StringUtils.isNullOrEmpty(searchText)) {
                lq.and(s -> s.like(ProductionWorkOrderExt::getProductionOrderCode, searchText).or()
                        .like(ProductionWorkOrderExt::getSkuNameAs, searchText).or()
                        .like(ProductionWorkOrderExt::getProductionSeqItemName, searchText).or()
                        .like(ProductionWorkOrderExt::getProductionSeqItemCode, searchText));
            }
        }

        lq.orderByAsc(ProductionWorkOrderExt::getProductionSeqItemCode);
        return productionWorkOrderExtMapper.selectPage(page, lq);

    }

    @Override
    public List<ProductionWorkOrderExt> query(String productionOrderItemId) {
        return baseMapper.selectByItemId(productionOrderItemId);
    }

    @Override
    public List<ProductionWorkOrderExt> query(String productionOrderItemId, String seqItemId) {
        return baseMapper.selectByItemAndSeq(productionOrderItemId, seqItemId);
    }

    @Override
    public List<ProductionWorkOrderOutExt> queryOut(String productionOrderItemId, String productionOrderCode) {
        return baseMapper.selectOutByItemId(productionOrderItemId, productionOrderCode);
    }

    @Override
    public ProductionWorkOrderExt queryViewById(String id) {
        return productionWorkOrderExtMapper.selectById(id);
    }
}